<template>
  <div class="zk-table">
    <div class="top-bar">
      <div class="left">
        <slot name="left-top" />
      </div>
      <div class="right">
        <slot name="right-top" />
        <el-button
          v-if="hasAddAction"
          icon="el-icon-plus"
          type="primary"
          plain
          size="medium"
          @click="defaultActionOptions.add.func()"
        >{{ defaultActionOptions.add.text }}
        </el-button>
      </div>
    </div>
    <el-table
      ref="zkTable"
      stripe
      :data="propTableData.length ? propTableData : tableData"
      :min-height="height"
      :height="height"
      tooltip-effect="dark"
      row-class-name="row-class-name"
      cell-class-name="cell-class-name"
      header-row-class-name="header-row-class-name"
      header-cell-class-name="header-cell-class-name"
      style="width:100%"
      :max-height="800"
      :row-key="codeKey"
      :header-cell-style="{ background:'#F0F0F0' }"
      @selection-change="selectedChangeHandle"
    >
      <el-table-column
        v-if="useSelection"
        reserve-selection
        align="center"
        type="selection"
        width="80"
      />
      <el-table-column
        v-if="useIndex"
        type="index"
        align="center"
        label="序号"
        width="80"
      />
      <template v-for="column of columns">
        <el-table-column
          v-if="column.slot"
          :key="column.key"
          :prop="column.key"
          show-overflow-tooltip
          :label="column.label"
          :width="column.width || 'auto'"
          :min-width="(column.label || '').length * 16 + 30"
          :align="column.align || 'center'"
        >
          <template slot-scope="scope">
            <slot :name="column.key" :row="scope.row" :index="scope.$index"></slot>
          </template>
        </el-table-column>
        <el-table-column
          v-else
          :key="column.key"
          :prop="column.key"
          :label="column.label"
          :width="column.width || 'auto'"
          :min-width="(column.label || '').length * 16 + 30"
          show-overflow-tooltip
          header-align="center"
          :align="column.align || 'center'"
          :formatter="column.render || (column.storeKey ? (row, { property }) => transferStoreName(column.storeKey, row[property]): undefined)"
        />
      </template>
      <el-table-column
        v-if="!!tableActions.length"
        :label="controlText"
        :width="controlColumnWidth"
        align="center"
        fixed="right"
      >
        <template slot-scope="scope">
          <slot v-if="controlUseSlot" name="control" :row="scope.row" :index="scope.$index"></slot>
          <div
            v-for="action of tableActions.filter(act=>filterAction(act,scope.row))"
            v-else
            :key="action.text"
            class="group-btn-item"
          >
            <el-popconfirm
              v-if="action.confirm"
              icon="el-icon-info"
              :title="action.confirmText || '请配置confirmText'"
              @onConfirm="action.func(scope.row,action.type)"
            >
              <el-button
                slot="reference"
                :class="action.classname"
                size="small"
                type="text"
              >{{ action.text }}
              </el-button>
            </el-popconfirm>
            <el-button
              v-else
              :class="action.classname"
              type="text"
              size="small"
              @click="action.func(scope.row, action.type)"
            >{{ action.text }}
            </el-button>
          </div>
        </template>
      </el-table-column>
    </el-table>
    <div class="bottom-bar">
      <div class="left">
        <div
          v-if="useSelection"
          class="selected-count"
        >已经选中 {{ selectedCount }} 条记录
        </div>
        <slot name="left-bottom" />
      </div>
      <div class="right">
        <slot name="right-bottom" />
        <zk-pagination
          v-if="usePagination"
          v-model="pagination"
          :total="total"
        />
      </div>
    </div>
  </div>
</template>
<script>
  import { mapGetters } from 'vuex'
  import ZkPagination from '@/components/zk-pagination'
  import { START_PAGE } from '@/common/zk-constants'
  import moment from 'moment'

  export default {
    name: 'ZkTable',
    components: { ZkPagination },
    inject: {
      provideDisabled: {
        type: Boolean,
        default: false,
      }
    },
    props: {
      columns: {
        type: Array,
        required: true,
        default: () => []
      },
      propTableData: {
        type: Array,
        default: () => []
      },
      actions: {
        type: Array,
        required: false,
        default: () => []
      },
      controlText: {
        type: String,
        default: '操作',
      },
      query: {
        type: Object,
        default: () => ({}),
      },
      service: {
        type: Object,
        default: () => ({}),
      },
      doOtherAction: {
        type: Function,
        default: new Function(),
      },
      codeKey: {
        type: String,
        default: 'id',
      },
      height: {
        type: null,
        // default: 528,
        default: 620,
      },
      useIndex: {
        type: Boolean,
        default: false,
      },
      useSelection: {
        type: Boolean,
        default: false,
      },
      usePagination: {
        type: Boolean,
        default: true,
      },
      onSelectionChange: {
        type: Function,
        default: null
      },
      requestInterceptor: {
        type: Function,
        default: query => query,
      },
      responseInterceptor: {
        type: Function,
        default: resp => resp,
      }
    },
    data() {
      return {
        defaultActionOptions: {},
        pagination: {
          currentPage: 1,
          pageSize: 10,
        },
        total: 0,
        tableData: [],
        controlWidth: null,
        selectedCount: 0,
      }
    },
    computed: {
      ...mapGetters(['dictionaries']),
      hasAddAction() {
        return !!this.actions.find(e => e === 'add')
      },
      tableActions() {
        if (this.actions.find(e => e === 'slot')) {
          return this.actions
        }
        return this.actions.filter(e => e !== 'add')
          .map(action => {
            // 查看模式默认筛选其他行为
            const type = typeof action === 'string' ? action : action.type
            if (this.provideDisabled && ['edit', 'add', 'del'].includes(type)) {
              return null
            }
            if (typeof action === 'string') {
              const actionOption = this.defaultActionOptions[action]
              return actionOption || {
                classname: 'info',
                text: action,
                type: action,
                func: (row, type) =>
                  this.$emit('doOtherAction', { type, row })
              }
            } else {
              action.func = (row, type) =>
                this.$emit('doOtherAction', { type, row })
              return action
            }
          }).filter(e => !!e)
      },
      controlUseSlot() {
        return !!this.actions.find(e => e === 'slot')
      },
      controlSlotWidth() {
        return this.controlUseSlot ? (this.actions.find(e => Number.isInteger(e)) || 100) : 0
      },
      controlColumnWidth() {
        return this.controlUseSlot ? this.controlSlotWidth
          : this.tableActions.reduce((total, action) => {
            return total + action.text.length * 20
          }, 40)
      }
    },
    watch: {
      query(newQuery) {
        this.refreshData(newQuery)
      },
      pagination(newValue, oldValue) {
        if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
          this.refreshData(newValue)
        }
      }
    },
    created() {
      this.id = this.$route.params[this.codeKey]
      const [{ path }] = this.$route.matched.slice(-2, -1)
      this.defaultActionOptions = {
        add: {
          classname: 'success',
          text: '新增',
          type: 'add',
          func: () => this.$router.push(`${path}/add`)
        },
        del: {
          classname: 'danger',
          type: 'del',
          text: '删除',
          confirm: true,
          confirmText: '确认删除',
          func: row => this.del(row)
        },
        edit: {
          classname: 'warning',
          type: 'type',
          text: '编辑',
          func: row => this.$router
            .push(`${path}/edit/${row[this.codeKey]}`)
        },
        review: {
          classname: 'info',
          type: 'review',
          text: '查看',
          func: row => this.$router
            .push(`${path}/review/${row[this.codeKey]}`)
        },
      }
      this.refreshData()
    },
    methods: {
      async refreshData(newQuery = {}) {
        if (!this.service || !this.service.getList) return
        const { query = {} } = this
        const queryBody = { ...query }
        // 容错处理
        Object.keys(queryBody).forEach(key => {
          if (!key || key.startsWith('_') ||
            ['', 'undefined', 'null'].includes(key)) {
            delete queryBody[key]
          }
          if (Object.prototype.toString.call(queryBody[key]) === '[object Date]') {
            queryBody[key] = moment(queryBody[key]).format('YYYY-MM-DD HH:mm:ss')
          }
        })
        const p = {
          ...queryBody,
          ...this.pagination,
          ...newQuery,
        }
        const params = this.requestInterceptor(p) || {}
        const responseData = await this.service.getList({
          ...params,
          currentPage: params.currentPage - (1 - START_PAGE)
        })
        const data = this.responseInterceptor(responseData) || []
        // console.log('wgm', responseData);
        if (!data) return
        if (Array.isArray(data)) {
          this.pagination.currentPage = 1
          this.pagination.pageSize = data.length
          this.tableData = data
          this.total = data.length
        } else {
          const { list, total, currentPage } = data
          this.pagination.currentPage = currentPage >= 0 ? currentPage + 1 - START_PAGE : p.currentPage
          this.tableData = list
          this.total = total
        }
        this.$refs.zkTable && this.$refs.zkTable.clearSelection()
        this.$emit('refresh-data', {
          "tableData": this.propTableData.length ? this.propTableData : this.tableData
        })
      },
      async del(row) {
        const ret = await this.service.delItem(row[this.codeKey])
        if (ret) {
          this.refreshData()
          this.$message.success('删除成功')
        }
      },
      selectedChangeHandle(values) {
        this.selectedCount = values.length
        this.$emit('onSelectionChange', values)
      },
      computedIndex(n) {
        const { currentPage, pageSize } = this.pagination
        return (1 + n) + (currentPage - 1) * pageSize
      },
      transferStoreName(storeKey, value) {
        return ((this.dictionaries[storeKey] || []).find(e => e.key == value) || {}).value || value
      },
      filterAction(action, row = {}) {
        const { filters } = action

        function checkValues(data = '', values) {
          if (values) {
            if (Array.isArray(values)) {
              return values.some(v => String(data) === String(v || ''))
            } else {
              return String(data) === String(values || '')
            }
          }
        }

        if (filters) {
          if (Array.isArray(filters)) {
            return filters.every(filter => {
              const { key, values } = filter
              if (key && values) {
                return checkValues(row[key], values)
              }
            })
          } else if (Object.prototype.toString.call(filters) === '[object Object]') {
            return Object.keys(filters).every(key => {
              const values = filters[key]
              if (values) {
                return checkValues(row[key], values)
              }
            })
          } else {
            console.error('filters is not array or obj')
            return false
          }
        } else {
          return true
        }
      },
      getTableData() {
        return this.propTableData.length ? this.propTableData : this.tableData
      }
    },
  }
</script>
<style scoped lang="scss">
  .zk-table {
    background: white;
    padding-bottom: 10px;
    width: 100%;

    .group-btn-item {
      display: inline;
      white-space: nowrap;
      padding: 0 1px;

      .el-button {
        margin: 0 4px;
      }
    }

    .add-section {
      margin-right: 20px;
    }

    .bottom-bar, .top-bar {
      display: flex;
      justify-content: space-between;
      padding: 10px;

      .left {
        display: flex;
        justify-content: flex-start;
        padding-left: 20px;
      }

      .right {
        display: flex;
        justify-content: flex-end;
        padding-right: 20px;
      }
    }

    .selected-count {
      $h: 50px;
      color: #ebeef5;
      font-size: 14px;
      height: $h;
      line-height: $h;
    }
  }
</style>
